package cn.song.freight.service.impl;

import cn.song.freight.domain.dto.StrategyAreaPricingDTO;
import cn.song.freight.domain.dto.StrategyConfigUnifiedSaveRequestDTO;
import cn.song.freight.domain.dto.WeightSegmentRuleDTO;
import cn.song.freight.domain.entity.StrategyAreaPricingDO;
import cn.song.freight.domain.result.R;
import cn.song.freight.service.PricingAreaMappingService;
import cn.song.freight.service.StrategyAreaPricingService;
import cn.song.freight.service.StrategyConfigUnifiedService;
import cn.song.freight.service.WeightSegmentRuleService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;

/**
 * 策略配置统一处理服务实现类
 * 负责处理策略配置的统一保存、更新、删除操作
 * 使用事务管理确保数据一致性
 *
 * @author SongRenShuo
 * @version 3.0.0
 * @since 2025-08-26
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class StrategyConfigUnifiedServiceImpl implements StrategyConfigUnifiedService {

    private final StrategyAreaPricingService strategyAreaPricingService;

    private final PricingAreaMappingService pricingAreaMappingService;

    private final WeightSegmentRuleService weightSegmentRuleService;

    /**
     * 统一处理所有策略配置变更 - V2版本（极简优化版）
     * <pre>
     * 采用"先删除后重建"的策略，极简化处理逻辑：
     *
     * 核心优化：
     * 1. 删除策略下的所有现有配置（区域定价配置、重量规则）
     * 2. 根据请求重新创建所有配置
     * 3. 任何失败直接抛异常，利用事务回滚
     * 4. 无需复杂的计数和错误收集
     *
     * 处理流程：
     * 1. 清理：删除策略下的所有现有配置
     * 2. 重建：根据请求创建新的配置
     * 3. 成功返回true，失败抛异常
     *
     * 优势：
     * - 逻辑极简，代码量最少
     * - 利用事务自动回滚，保证一致性
     * - 错误处理简单，维护成本最低
     *
     * @param requestDTO 简化结构的统一配置请求DTO
     * @return 成功返回true，失败抛异常
     * </pre>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> processAllConfigChangesV2(StrategyConfigUnifiedSaveRequestDTO requestDTO) {
        log.info("🚀 开始V2版本统一处理策略配置变更（极简优化版），策略ID：{}，操作人：{}",
                requestDTO.getStrategyId(), requestDTO.getOperator());

        // 记录请求数据统计
        logRequestDataStatisticsV2(requestDTO);

        // 步骤1：清理 - 删除策略下的所有现有配置
        cleanupAllExistingConfigsSimple(requestDTO.getStrategyId(), requestDTO.getOperator());

        // 步骤2：重建 - 根据请求创建新的配置
        rebuildAllConfigsSimple(requestDTO.getStrategyId(), requestDTO.getAreaConfigList(), requestDTO.getOperator());

        log.info("✅ V2版本统一配置处理完成，策略ID：{}", requestDTO.getStrategyId());
        return R.success(true);
    }

    /**
     * 记录V2版本请求数据统计信息
     */
    private void logRequestDataStatisticsV2(StrategyConfigUnifiedSaveRequestDTO requestDTO) {
        int totalAreaConfigs = requestDTO.getAreaConfigList() != null ? requestDTO.getAreaConfigList().size() : 0;
        int totalWeightRules = requestDTO.getAreaConfigList() != null ?
                requestDTO.getAreaConfigList().stream()
                        .mapToInt(areaConfig -> areaConfig.getWeightRuleList() != null ? areaConfig.getWeightRuleList().size() : 0)
                        .sum() : 0;

        log.info("📊 V2请求数据详情：区域配置[{}]，重量规则总数[{}]", totalAreaConfigs, totalWeightRules);
    }



    // ==================== V2版本极简优化方法 ====================

    /**
     * 清理策略下的所有现有配置（极简版本）
     * <pre>
     * 极简化的清理逻辑：
     * 1. 查询策略下所有区域配置
     * 2. 逐个删除，任何失败直接抛异常
     * 3. 无需计数，无需try-catch，利用事务回滚
     *
     * @param strategyId 策略ID
     * @param operator 操作人
     * @throws RuntimeException 删除失败时抛异常
     * </pre>
     */
    private void cleanupAllExistingConfigsSimple(Long strategyId, String operator) {
        log.info("🧹 开始清理策略[{}]下的所有现有配置...", strategyId);

        // 查询策略下的所有区域定价配置
        List<StrategyAreaPricingDO> existingConfigs = strategyAreaPricingService.getByStrategyId(strategyId);

        log.info("📊 策略[{}]下现有区域配置[{}]个", strategyId, existingConfigs.size());

        // 删除所有区域定价配置（级联删除相关数据）
        for (StrategyAreaPricingDO config : existingConfigs) {
            R<Boolean> deleteResult = strategyAreaPricingService.deletePricingConfig(config.getId());
            if (!deleteResult.isCode()) {
                String errorMsg = String.format("清理区域配置失败，ID[%d]: %s", config.getId(), deleteResult.getMsg());
                log.error("❌ {}", errorMsg);
                throw new RuntimeException(errorMsg);
            }
            log.info("✅ 清理区域配置成功，ID[{}]，名称[{}]", config.getId(), config.getName());
        }

        log.info("✅ 配置清理完成，共清理[{}]个配置", existingConfigs.size());
    }

    /**
     * 重建所有配置（极简版本）
     * <pre>
     * 极简化的重建逻辑：
     * 1. 遍历区域配置列表
     * 2. 逐个重建，任何失败直接抛异常
     * 3. 无需计数，无需try-catch，利用事务回滚
     *
     * @param strategyId 策略ID
     * @param areaConfigList 区域配置列表
     * @param operator 操作人
     * @throws RuntimeException 重建失败时抛异常
     * </pre>
     */
    private void rebuildAllConfigsSimple(Long strategyId,
            List<StrategyConfigUnifiedSaveRequestDTO.AreaConfigDTO> areaConfigList, String operator) {

        log.info("🔄 开始重建策略[{}]的所有配置...", strategyId);

        if (areaConfigList == null || areaConfigList.isEmpty()) {
            log.info("⚠️ 区域配置列表为空，跳过重建");
            return;
        }

        // 处理每个区域配置及其重量规则
        for (StrategyConfigUnifiedSaveRequestDTO.AreaConfigDTO areaConfig : areaConfigList) {
            log.info("🌍 重建区域配置，名称[{}]，地区数量[{}]",
                    areaConfig.getName(), areaConfig.getSelectedAreaIds().size());

            rebuildSingleAreaConfigSimple(areaConfig, strategyId, operator);

            log.info("✅ 重建区域配置成功");
        }

        log.info("✅ 配置重建完成，共重建[{}]个区域配置", areaConfigList.size());
    }

    /**
     * 重建单个区域配置（极简版本）
     * <pre>
     * 极简化的区域配置处理逻辑：
     * 1. 创建区域配置获得pricingId
     * 2. 如果有重量规则，创建重量规则
     * 3. 任何失败直接抛异常，无需返回值
     *
     * @param areaConfig 区域配置
     * @param strategyId 策略ID
     * @param operator 操作人
     * @throws RuntimeException 处理失败时抛异常
     * </pre>
     */
    private void rebuildSingleAreaConfigSimple(StrategyConfigUnifiedSaveRequestDTO.AreaConfigDTO areaConfig,
                                             Long strategyId, String operator) {
        // 创建区域配置获得pricingId
        Long pricingId = rebuildAreaConfigSimple(areaConfig, strategyId, operator);

        // 如果有重量规则，创建重量规则
        if (areaConfig.getWeightRuleList() != null && !areaConfig.getWeightRuleList().isEmpty()) {
            for (StrategyConfigUnifiedSaveRequestDTO.WeightRuleDTO weightRule : areaConfig.getWeightRuleList()) {
                WeightSegmentRuleDTO ruleDTO = new WeightSegmentRuleDTO();
                ruleDTO.setId(null); // 强制新增
                ruleDTO.setStrategyId(strategyId);
                ruleDTO.setPricingId(pricingId);
                ruleDTO.setUpperBound(BigDecimal.valueOf(weightRule.getUpperBound()));
                ruleDTO.setFreight(BigDecimal.valueOf(weightRule.getFreight()));
                ruleDTO.setOperator(operator);

                R<WeightSegmentRuleDTO> result = weightSegmentRuleService.add(ruleDTO);

                if (!result.isCode()) {
                    String errorMsg = String.format("重建重量规则失败: %s", result.getMsg());
                    log.error("❌ {}", errorMsg);
                    throw new RuntimeException(errorMsg);
                }

                WeightSegmentRuleDTO resultData = result.getData();
                Long ruleId = resultData != null ? resultData.getId() : null;
                log.info("✅ 重建重量规则成功，ID[{}]，关联pricing_id[{}]", ruleId, pricingId);
            }
        }
    }

    /**
     * 重建区域配置（极简版本）
     * <pre>
     * 极简化的区域配置创建逻辑：
     * 1. 创建定价配置
     * 2. 建立区域映射关系
     * 3. 任何失败直接抛异常
     *
     * @param areaConfig 区域配置
     * @param strategyId 策略ID
     * @param operator 操作人
     * @return 新创建的定价配置ID
     * @throws RuntimeException 处理失败时抛异常
     * </pre>
     */
    private Long rebuildAreaConfigSimple(StrategyConfigUnifiedSaveRequestDTO.AreaConfigDTO areaConfig,
                                       Long strategyId, String operator) {
        // 构建定价配置DTO（忽略原有ID）
        StrategyAreaPricingDTO pricingDTO = new StrategyAreaPricingDTO();
        pricingDTO.setId(null); // 强制新增
        pricingDTO.setStrategyId(strategyId);
        pricingDTO.setName(areaConfig.getName());
        pricingDTO.setDescription(areaConfig.getDescription());
        pricingDTO.setFirstWeightKg(areaConfig.getFirstWeightKg() != null ?
                BigDecimal.valueOf(areaConfig.getFirstWeightKg()) : BigDecimal.ZERO);
        pricingDTO.setFirstWeightFee(areaConfig.getFirstWeightFee() != null ?
                BigDecimal.valueOf(areaConfig.getFirstWeightFee()) : BigDecimal.ZERO);
        pricingDTO.setAdditionalWeightKg(areaConfig.getAdditionalWeightKg() != null ?
                BigDecimal.valueOf(areaConfig.getAdditionalWeightKg()) : BigDecimal.ZERO);
        pricingDTO.setAdditionalWeightFee(areaConfig.getAdditionalWeightFee() != null ?
                BigDecimal.valueOf(areaConfig.getAdditionalWeightFee()) : BigDecimal.ZERO);
        pricingDTO.setVolumetricWeightRatio(areaConfig.getVolumetricWeightRatio() != null ?
                BigDecimal.valueOf(areaConfig.getVolumetricWeightRatio()) : BigDecimal.ZERO);
        pricingDTO.setOperator(operator);

        // 创建定价配置
        R<Long> addResult = strategyAreaPricingService.addPricingConfig(pricingDTO);

        if (!addResult.isCode()) {
            String errorMsg = String.format("重建区域定价配置失败: %s", addResult.getMsg());
            log.error("❌ {}", errorMsg);
            throw new RuntimeException(errorMsg);
        }

        Long pricingId = addResult.getData();

        // 建立区域映射关系
        boolean mappingResult = pricingAreaMappingService.resetAreaMapping(
                pricingId, areaConfig.getSelectedAreaIds(), operator);

        if (!mappingResult) {
            String errorMsg = String.format("重建区域映射失败，定价配置ID[%d]", pricingId);
            log.error("❌ {}", errorMsg);
            throw new RuntimeException(errorMsg);
        }

        log.info("✅ 重建区域配置成功，定价配置ID[{}]，地区数量[{}]",
                pricingId, areaConfig.getSelectedAreaIds().size());
        return pricingId;
    }


}
